package org.biubiu0002.baiblog.core.service.impl;

import com.alibaba.fastjson.JSON;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import org.apache.commons.lang3.StringUtils;
import org.biubiu0002.baiblog.common.constants.CacheConstants;
import org.biubiu0002.baiblog.common.exception.BaiBlogException;
import org.biubiu0002.baiblog.common.exception.BizCodeEnumException;
import org.biubiu0002.baiblog.common.utils.PageUtils;
import org.biubiu0002.baiblog.common.utils.Query;
import org.biubiu0002.baiblog.core.async.event.UpdateUserStatusEvent;
import org.biubiu0002.baiblog.core.component.RabbitMqService;
import org.biubiu0002.baiblog.core.dao.SysUserDao;
import org.biubiu0002.baiblog.core.model.dto.TokenUserDto;
import org.biubiu0002.baiblog.core.model.entity.ShareEntity;
import org.biubiu0002.baiblog.core.model.entity.SysUserEntity;
import org.biubiu0002.baiblog.core.model.entity.SysUserRoleEntity;
import org.biubiu0002.baiblog.core.model.vo.UserInfoVo;
import org.biubiu0002.baiblog.core.model.vo.req.UpdateUserStatusReqVo;
import org.biubiu0002.baiblog.core.model.vo.req.UserUpdateReqVo;
import org.biubiu0002.baiblog.core.model.vo.res.LoginResVo;
import org.biubiu0002.baiblog.core.service.*;
import org.biubiu0002.baiblog.core.utils.IdGenerator;
import org.biubiu0002.baiblog.core.utils.SecurityUtils;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.CollectionUtils;

import javax.annotation.Resource;
import java.util.*;
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;


@Service("sysUserService")
public class SysUserServiceImpl extends ServiceImpl<SysUserDao, SysUserEntity> implements SysUserService {

    @Resource
    private AuthService authService;

    @Resource
    private SysUserRoleService sysUserRoleService;

    @Resource
    private SysRoleService sysRoleService;

    @Resource
    private FriendshipService friendshipService;

    @Resource
    private ShareService shareService;

    @Resource
    private SysUserDao sysUserDao;

    @Autowired
    private StringRedisTemplate redisTemplate;

    @Resource
    private RabbitMqService rabbitMqService;





    @Override
    public PageUtils queryPage(Map<String, Object> params) {
        QueryWrapper<SysUserEntity> wrapper = new QueryWrapper<>();
        String userUid = (String) params.get("userUid");
        String phone = (String) params.get("phone");
        String email = (String) params.get("email");
        String nickname = (String) params.get("nickname");
        if(StringUtils.isNotBlank(userUid)){
            wrapper.and(w-> w.eq("resource_id",userUid));
        }
        if(StringUtils.isNotBlank(phone)){
            wrapper.and(w-> w.eq("phone",phone));
        }
        if(StringUtils.isNotBlank(email)){
            wrapper.and(w-> w.eq("email",email));
        }
        if(StringUtils.isNotBlank(nickname)){
            wrapper.and(w->{
                w.eq("nickname",nickname).or().like("nickname",nickname);
            });
        }

        IPage<SysUserEntity> page = this.page(
                new Query<SysUserEntity>().getPage(params),
                wrapper
        );

        return new PageUtils(page);
    }

    @Override
    public SysUserEntity getUserByUserUid(String userUid) {
        return this.getOne(new QueryWrapper<SysUserEntity>()
                .eq("resource_id", userUid)
                .eq("is_deleted", 0)
        );

    }

    @Override
    public List<String> getAllPermissionByUserUid(String userUid) {
        List<Integer> roles = this.getRolesByUserUid(userUid);
        if(CollectionUtils.isEmpty(roles)){
            return Collections.emptyList();
        }
        return sysRoleService.getResourceMethod(roles);
    }

    @Override
    public UserInfoVo getUserInfoByUserUid(String userUid) {
        String userInfoVoJson = redisTemplate.opsForValue().get(CacheConstants.getUserInfoKey(userUid));
        if(StringUtils.isBlank(userInfoVoJson)){
            //缓存没有 从db读
            SysUserEntity userByUserUid = this.getUserByUserUid(userUid);
            if(userByUserUid==null){
                throw new BaiBlogException(BizCodeEnumException.RECORD_NOT_EXIST_EXCEPTION,"用户不存在");
            }
            UserInfoVo userInfoVo = new UserInfoVo();
            BeanUtils.copyProperties(userByUserUid,userInfoVo);
            userInfoVo.setUserUid(userByUserUid.getResourceId());
            userInfoVoJson  = JSON.toJSONString(userInfoVo);
            //写回redis
            redisTemplate.opsForValue().set(CacheConstants.getUserInfoKey(userUid),userInfoVoJson ,
                    CacheConstants.getUserInfoExpireMinutes(),TimeUnit.MINUTES);
        }
        if(StringUtils.isBlank(userInfoVoJson)){
            return null;
        }
        UserInfoVo userInfoVo = JSON.parseObject(userInfoVoJson, UserInfoVo.class);

        //好友关系
        assembleFriendship(userInfoVo,userUid);
        return userInfoVo;
    }

    @Override
    @Transactional(rollbackFor = BaiBlogException.class)
    public Map<String,UserInfoVo> getUserInfoMap(List<String> userUidList) {
        if(CollectionUtils.isEmpty(userUidList)){
            log.error("传入参数为空");
            return Collections.emptyMap();
        }
        Map<String,UserInfoVo> res = new HashMap<>(16);
        List<String> redisNotExistedUidList =new ArrayList<>();

        //从redis读 不存在则从db读
        for (String userUid : userUidList) {
            String userInfoJson = redisTemplate.opsForValue().get(userUid);
            if(StringUtils.isNotBlank(userInfoJson)){
                UserInfoVo userInfoVoFromRedis = JSON.parseObject(userInfoJson, UserInfoVo.class);
                //assemble好友关系
                assembleFriendship(userInfoVoFromRedis,userUid);
                res.put(userUid,userInfoVoFromRedis);
            }
            else{
                //redis中不存在
                redisNotExistedUidList.add(userUid);
            }
        }

        if(!CollectionUtils.isEmpty(redisNotExistedUidList)){
            //从数据库中获取对应的用户信息
            List<SysUserEntity> userEntityListFromDB = this.getUsersByUidList(redisNotExistedUidList);
            Map<String, UserInfoVo> userInfoVoFromDB = userEntityListFromDB.stream().collect(Collectors.toMap(SysUserEntity::getResourceId, e -> {
                UserInfoVo userInfoVo = new UserInfoVo();
                BeanUtils.copyProperties(e, userInfoVo);
                userInfoVo.setUserUid(e.getResourceId());
                return userInfoVo;
            }, (oldV, newV) -> newV));
            //assemble好友关系
            this.assembleFriendship(userInfoVoFromDB);
            res.putAll(userInfoVoFromDB);

            //写回redis
            userInfoVoFromDB.forEach((uid,userInfoVo)->{
                redisTemplate.opsForValue().set(CacheConstants.getUserInfoKey(uid),JSON.toJSONString(userInfoVo),
                        CacheConstants.getUserInfoExpireMinutes(), TimeUnit.MINUTES);
            });
        }
        return res;
    }

    public void assembleFriendship(UserInfoVo userInfoVo,String userUid){
        //好友关系
        int fansCount = friendshipService.fansCount(userUid);
        int followCount = friendshipService.followCount(userUid);
        userInfoVo.setFollowersCount(fansCount);
        userInfoVo.setFollowingCount(followCount);
        TokenUserDto user = SecurityUtils.getUser();
        if(user!=null){
            //当前用户是否关注了uid
            int following = friendshipService.isFollowing(userUid);
            //当前用户是否被uid关注
            int followed = friendshipService.isFollowed(userUid);
            userInfoVo.setFollowing(following>0);
            userInfoVo.setFollowMe(followed>0);
        }
        userInfoVo.setFollowingCount(followCount);
        userInfoVo.setFollowersCount(fansCount);
    }

    public void assembleFriendship(Map<String,UserInfoVo> map){
        map.forEach((userUid,userInfoVo)->{
            assembleFriendship(userInfoVo,userUid);
        });
    }

    @Override
    public List<String> getRoleNamesByUserUid(String userUid) {
        if(StringUtils.isBlank(userUid)){
            return Collections.emptyList();
        }
        return sysUserDao.selectRolesByUserUid(userUid);
    }

    @Override
    public List<SysUserEntity> getUsersByUidList(List<String> redisNotExistedUidList) {
        if(CollectionUtils.isEmpty(redisNotExistedUidList)){
            return Collections.emptyList();
        }
        return this.baseMapper.selectList(new QueryWrapper<SysUserEntity>()
                .in("resource_id", redisNotExistedUidList)
                .eq("status", 0)
                .eq("is_deleted", 0)
        );
    }

    @Override
    public List<Integer> getRolesByUserUid(String userUid){
        List<SysUserRoleEntity> userRoleEntities = sysUserRoleService.getBaseMapper().selectList(new QueryWrapper<SysUserRoleEntity>().eq("user_uid", userUid));
        //判空？ 如果用户的类型被删光了 会不会报错
        return userRoleEntities.stream().map(SysUserRoleEntity::getRoleId).collect(Collectors.toList());
    }

    @Override
    @Transactional(rollbackFor = BaiBlogException.class)
    public LoginResVo updateUser(UserUpdateReqVo userUpdateReqVo) {
        TokenUserDto user = SecurityUtils.getUser();
        if(user==null){
            throw new BaiBlogException(BizCodeEnumException.NOT_LOGIN_EXCEPTION);
        }
        String userUid = user.getUserUid();
        SysUserEntity sysUserEntity = new SysUserEntity();
        BeanUtils.copyProperties(userUpdateReqVo,sysUserEntity);
        sysUserEntity.setResourceId(userUid);
        sysUserDao.updateUser(sysUserEntity);
        //删除用户信息缓存
        redisTemplate.delete(CacheConstants.getUserInfoKey(userUid));
        //删除用户发布过的推文缓存
        List<ShareEntity> shareEntityList = shareService.getShareListByUserUid(userUid);
        List<String> shareIdList = shareEntityList.stream().map(ShareEntity::getResourceId).collect(Collectors.toList());
        List<String> shareInfoKeys = shareIdList.stream().map(CacheConstants::getShareInfoKey).collect(Collectors.toList());
        redisTemplate.delete(shareInfoKeys);
        //删除旧token的内容
        String oldToken = user.getToken();
        redisTemplate.delete(CacheConstants.getTokenKey(oldToken));
        //生成新token
        String token = IdGenerator.generateUid();
        //构造loginResVo并返回
        SysUserEntity userByUserUid = getUserByUserUid(userUid);
        LoginResVo loginResVo = new LoginResVo();
        BeanUtils.copyProperties(userByUserUid,loginResVo);
        loginResVo.setUserUid(userUid);
        loginResVo.setToken(token);
        loginResVo.setRoles(this.getRoleNamesByUserUid(userUid));
        TokenUserDto tokenUserDto = new TokenUserDto();
        BeanUtils.copyProperties(loginResVo,tokenUserDto);
        tokenUserDto.setUserUid(userUid);
        //redis设置新token
        authService.setTokenUser(tokenUserDto);
        return loginResVo;
    }

    @Override
    public void updateUserStatus(UpdateUserStatusReqVo updateUserStatusReqVo) {
        String userUid = updateUserStatusReqVo.getUserUid();
        Integer oldStatus = getUserByUserUid(userUid).getStatus();
        Integer newStatus = updateUserStatusReqVo.getStatus();
        if(!newStatus.equals(oldStatus)){
            sysUserDao.updateUserStatus(userUid,newStatus);
            //发送mq
            UpdateUserStatusEvent updateUserStatusEvent = new UpdateUserStatusEvent(userUid, oldStatus, newStatus);
            rabbitMqService.sendUpdateUserStatusEvent(updateUserStatusEvent);
        }

    }

    public void updateByUserUid(SysUserEntity user){
        this.update(user,new QueryWrapper<SysUserEntity>().eq("resource_id",user.getResourceId()));
    }




}